/**
在 React 中， `ref`  是一个用于直接访问 DOM 元素或类组件实例的特殊属性。通过  `ref` ，开发者可以在 React 组件中获取对 DOM 元素或组件实例的引用，以便进行操作。
  1. 基本概念
  -  `ref` ：可以用来获取对 DOM 元素或 React 组件实例的引用。通过  `ref` ，可以直接访问和操作这些元素或实例。

  2. 创建  `ref` 
  使用  `React.createRef()`  创建一个  `ref`  对象。通常在类组件的构造函数中进行初始化。
   示例：
  jsx
  class MyComponent extends React.Component {
    constructor(props) {
      super(props);
      this.myRef = React.createRef(); // 创建 ref
    }

    render() {
      return <div ref={this.myRef}>Hello, World!</div>; // 绑定 ref
    }
  }
  3. 访问 DOM 元素

  在类组件中，可以通过  `this.myRef.current`  访问到实际的 DOM 元素。例如，可以在  `componentDidMount`  生命周期方法中访问和操作 DOM。

   示例：
  jsx
  componentDidMount() {
    console.log(this.myRef.current); // 访问 DOM 元素
    this.myRef.current.focus(); // 例如，设置焦点
  }
  4. 访问组件实例

  如果  `ref`  被绑定到类组件上，则可以通过  `this.myRef.current`  访问到该组件的实例。这允许调用该组件的方法。

   示例：
  jsx
  class ChildComponent extends React.Component {
    alertMessage() {
      alert('Hello from Child Component!');
    }

    render() {
      return <div>Child Component</div>;
    }
  }

  class ParentComponent extends React.Component {
    constructor(props) {
      super(props);
      this.childRef = React.createRef(); // 创建 ref
    }

    callChildMethod = () => {
      this.childRef.current.alertMessage(); // 调用子组件的方法
    };

    render() {
      return (
        <div>
          <ChildComponent ref={this.childRef} />
          <button onClick={this.callChildMethod}>Call Child Method</button>
        </div>
      );
    }
  }
  5. 函数组件中的  `ref` 

  在函数组件中，可以使用  `useRef`  Hook 来创建和使用  `ref` 。

   示例：
  jsx
  import React, { useRef, useEffect } from 'react';

  const MyFunctionalComponent = () => {
    const myRef = useRef(null); // 创建 ref

    useEffect(() => {
      console.log(myRef.current); // 访问 DOM 元素
      myRef.current.focus(); // 设置焦点
    }, []);

    return <input ref={myRef} type="text" />;
  };
  6. 注意事项

  - 不适合用于数据流： `ref`  主要用于访问 DOM 元素或组件实例，而不是用于数据流。应优先使用 props 和 state 进行数据管理。
  - 避免过度使用：过度使用  `ref`  可能导致组件的可维护性变差，应该在必要时使用。
  - 不支持函数组件的  `ref` ：在函数组件中， `ref`  只能用于 DOM 元素，不能直接用于函数组件本身。如果需要访问函数组件的实例，通常需要将其转换为类组件。

  7. 总结
  `ref`  是 React 中用于访问和操作 DOM 元素或组件实例的强大工具。通过  `createRef`  和  `useRef` ，开发者可以在类组件和函数组件中灵活使用  `ref` 。然而，应谨慎使用  `ref` ，优先考虑使用 React 的数据流机制，以保持组件的可维护性和可读性。
 */
import React, { createRef, PureComponent, forwardRef } from "react";

class ChildComponent extends PureComponent {
  test() {
    console.log("Child Component Method Called");
  }

  render() {
    return <div>Child Component</div>;
  }
}

// forwardRef: 将 ref 转发到子组件的 DOM 元素上
const ChildFnCopmponent = forwardRef(function (props, ref) {
  return (
    <div>
      <div ref={ref}>Child Component</div>
      <p>childFnCopmponent</p>
    </div>
  );
});

export default class App extends PureComponent {
  constructor() {
    super();

    this.state = {};
    this.titleEl = null;
    this.titleRef = createRef(); // 创建ref对象
    this.childCpnsRef = createRef();
    this.childCpnsFnRef = createRef();
  }

  getnativeDOM() {
    // 1. 在React元素上绑定一个ref字符串
    // console.log(this.refs.div); // 废弃了

    // 2. 提前创建好ref对象, createRef(), 将创建出来的对象绑定到元素
    console.log(this.titleRef.current);

    // 3. 回调函数方式, 将创建出来的对象绑定到元素
    console.log(this.titleEl);
  }

  getChildComponentMethod() {
    console.log(this.childCpnsRef.current);
    this.childCpnsRef.current.test();
  }

  getFnComponent() {
    console.log(this.childCpnsFnRef.current);
  }

  render() {
    return (
      <div>
        <h3>1. ref获取DOM</h3>
        {/* <div ref="div">ref获取dom createRef()方法1</div> */}
        <div ref={this.titleRef}>ref获取dom createRef()方法2</div>
        <div ref={(el) => (this.titleEl = el)}>ref获取dom createRef()方法3</div>
        <button onClick={(e) => this.getnativeDOM()}>展示DOM</button>

        <h3>2. 获取类组件实例</h3>
        <ChildComponent ref={this.childCpnsRef} />
        <button onClick={(e) => this.getChildComponentMethod()}>
          调用子组件方法
        </button>

        <h3>3. 获取函数组件实例</h3>
        <ChildFnCopmponent ref={this.childCpnsFnRef} />
        <button onClick={(e) => this.getFnComponent()}>获取函数组件实例</button>
      </div>
    );
  }
}
